package com.cx.healthex.base.dao;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.CollectionUtils;

import com.cx.healthex.base.dao.BaseDAO;
import com.cx.healthex.base.apiModel.PageModel;

/**
 * 通过范型和动态对象，持久化CRUD基本操作
 * 
 * @author Hungel zou
 * @since 2014年5月13日 下午4:50:45
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class AbstractBaseDao extends BaseDAO{

	protected final Logger log = LoggerFactory.getLogger(AbstractBaseDao.class);
	
	protected DBColumnMapper mapper = new DBColumnMapper();
	
	// java对类的序列化定义
	private static final String SERIAl = "serialVersionUID";
	
	protected String getSqlById(String tableName, String id){
		return "select * from "+ tableName + " where " + id + "=?";
	}
	
	/**
	 * 忽略的属性字段
	 * 
	 * @param field
	 * @param excludingNameArray
	 * @return
	 */
	private boolean isIgnoreField(String field, String[] excludingNameArray) {
		for(String excludingName : excludingNameArray) {
			if(field.equalsIgnoreCase(excludingName)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 反射类的属性值，并存储到map中
	 * 
	 * @param clazz
	 * @param obj
	 * @param paramMap
	 * @param excludingName
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> void invokeClass(Class<?> clazz, T obj, Map<String, Object> paramMap,String excludingName) throws IllegalArgumentException, IllegalAccessException{
		invokeClazz(clazz, obj, paramMap, new String[]{excludingName});
	}
	
	/**
	 * 反射类的属性值，并存储到map中
	 * 
	 * @param clazz
	 * @param obj
	 * @param paramMap
	 * @param excludingNameArray
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	private <T> void invokeClazz(Class<?> clazz, T obj, Map<String, Object> paramMap,String[] excludingNameArray) throws IllegalArgumentException, IllegalAccessException{
		if (clazz != null) {
			Field[] f = clazz.getDeclaredFields();
			if (f != null && f.length > 0) {
				for(Field field : f) {
					String fieldName = field.getName();
					boolean isIgnore = (excludingNameArray!=null && excludingNameArray.length > 0) ? !isIgnoreField(fieldName, excludingNameArray) : true;
					if(!SERIAl.equalsIgnoreCase(fieldName) && isIgnore) {
						field.setAccessible(true);
						Object val = field.get(obj);
						Date now = new Date();
						if (field.getName().equals("createBy") && val == null) {//处理默认值
							val = 0;
						} else if (field.getName().equals("updateBy") && val == null) {//处理默认值
							val = 0;
						} else if (field.getName().equals("createTime") && val == null) {//处理默认值
							val = now;
						} else if (field.getName().equals("updateTime") && val == null) {//处理默认值
							val = now;
						}
						paramMap.put(field.getName(), val);
						field.setAccessible(false);
					}
				}
			}
		}
	}
	
	/**
	 * 通过类反射生成insert脚本
	 * 
	 * @param clazz
	 * @param tableName
	 * @param excludingName
	 * @return
	 */
	protected StringBuilder buildInsertSql(Class<?> clazz,String tableName, String excludingName){
		if(StringUtils.isNotBlank(excludingName)){
			return buildInsertSql(clazz, tableName, new String[]{excludingName});
		} else
			return null;
	}
	
	/**
	 * 通过类反射生成insert脚本
	 * 
	 * @param clazz
	 * @param tableName
	 * @param excludingName
	 * @return
	 */
	private StringBuilder buildInsertSql(Class<?> clazz,String tableName, String[] excludingNameArray){
		List<ColumnMapper> columnMapperList = mapper.getColumnMapper(clazz,true);
		if (!CollectionUtils.isEmpty(columnMapperList)) {
			int size = columnMapperList.size();
			StringBuilder sql = new StringBuilder();
			
			StringBuilder val = new StringBuilder();
			sql.append("insert into ").append(tableName).append("(");
			
			for(int index = 0; index < size; index++) {
				ColumnMapper mapper = columnMapperList.get(index);
				if(isIgnoreField(mapper.getFiled(), excludingNameArray)) {
					continue;
				}
				sql.append(mapper.getColumn());
				
				val.append(":");
				val.append(mapper.getFiled());
				
				if(index != size - 1) {
					sql.append(",");
					val.append(",");
				}
			}
			
			sql.append(") values (");
			sql.append(val);
			sql.append(")");
			return sql;
		}
		return null;
	}
	
	/**
	 * 通过类反射生成update脚本
	 * 
	 * @param clazz
	 * @param tableName
	 * @param idColumnName
	 * @return
	 */
	private StringBuilder buildUpdateSql (Class<?> clazz,String tableName, String idFieldName){
		List<ColumnMapper> columnMapperList = mapper.getColumnMapper(clazz,true);
		
		StringBuilder sql = new StringBuilder();
		sql.append("UPDATE ").append(tableName).append(" SET ");
		
 		String idColumnName = "";
		for(int index = 0; index < columnMapperList.size(); index++) {
			ColumnMapper mapper = columnMapperList.get(index);
			if(mapper.getFiled().equalsIgnoreCase(idFieldName)) {
				idColumnName = mapper.getColumn();
				continue;
			}
			sql.append(mapper.getColumn());
			sql.append("=:").append(mapper.getFiled());
			if(index != columnMapperList.size() - 1) {
				sql.append(",");
			}
		}
		if(idFieldName != null && StringUtils.isNotEmpty(idFieldName)){
			sql.append(" WHERE ").append(idColumnName).append("=:").append(idFieldName);
		}
		return sql;
	}
	
	/**
	 * 
	 * @param obj
	 * @param tableName
	 * @param excludingNameArray
	 * @param needParent
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> boolean insert(T obj, String tableName, String[] excludingNameArray, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		Class<?> clazz = obj.getClass();
		// 通过class映射成insert脚本
		StringBuilder sql = buildInsertSql(clazz, tableName, excludingNameArray);
		Map<String, Object> paramMap = new HashMap<String, Object>();
		// 加载本类属性
		invokeClazz(clazz, obj, paramMap, excludingNameArray);
		if(needParent){
			// 加载父类属性
			invokeClazz(clazz.getSuperclass(), obj, paramMap, excludingNameArray);
		}
		
		int rowNum = jdbcTemplate.update(sql.toString(), paramMap);
		return rowNum > 0;
	}
	
	private <T> KeyHolder insertAndReturnPrimaryKey(T obj, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		Class<?> clazz = obj.getClass();
		// 通过class映射成insert脚本
		StringBuilder sql = buildInsertSql(clazz, tableName, excludingName);
		Map<String, Object> paramMap = new HashMap<String, Object>();
		// 加载本类属性
		invokeClass(clazz, obj, paramMap, excludingName);
		if(needParent){
			// 加载父类属性
			invokeClass(clazz.getSuperclass(), obj, paramMap, excludingName);
		}
		
		SqlParameterSource paramSource = new BeanPropertySqlParameterSource(obj);
		KeyHolder holder = new GeneratedKeyHolder();
		int rowNum = jdbcTemplate.update(sql.toString(), paramSource, holder);
		if(rowNum > 0) {
			return holder;
		}
		return null;
	}
	
	/**
	 * 新增操作并返回主键id
	 * 
	 * @param obj
	 * @param tableName
	 * @param excludingName
	 * @param needParent
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> Integer insertAndReturnId(T obj, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		KeyHolder holder = insertAndReturnPrimaryKey(obj, tableName, excludingName, needParent);
		if (holder != null) {
			return holder.getKey().intValue();
		}
		return null;
	}
	
	/**
	 * 新增操作并返回主键id
	 * 
	 * @param obj
	 * @param tableName
	 * @param excludingName
	 * @param needParent
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> Long insertAndReturnLongId(T obj, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		KeyHolder holder = insertAndReturnPrimaryKey(obj, tableName, excludingName, needParent);
		if (holder != null) {
			return holder.getKey().longValue();
		}
		return null;
	}
	
	/**
	 * 新增操作
	 * 
	 * @param obj ：待保存的对象
	 * @param tableName : 数据表名
	 * @param excludingName ：不包含的字段，如主键是自增的，可不需要出现在insert语句中
	 * @param needParent : 是否需要父类的属性转换成insert语句中的变量
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> boolean insert(T obj, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		return insert(obj, tableName, new String[]{excludingName}, needParent);
	}
	
	/**
	 * 新增操作
	 * 
	 * @param obj ：待保存的对象
	 * @param tableName : 数据表名
	 * @param excludingName ：不包含的字段，如主键是自增的，可不需要出现在insert语句中
	 * @param needParent : 是否需要父类的属性转换成insert语句中的变量
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> boolean add(T obj, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		return insert(obj, tableName, excludingName, needParent);
	}
	
	/**
	 * 新增操作，并返回主键
	 * 
	 * @param obj
	 * @param tableName
	 * @param excludingName
	 * @param needParent ： 是否需要父类的属性值
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> Integer addAndReturnId(T obj, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		return insertAndReturnId(obj, tableName, excludingName, needParent);
	}
	
	/**
	 * 更新操作
	 * 
	 * @param obj
	 * @param tableName
	 * @param idColumnName
	 * @param needParent ： 是否需要父类的属性值
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> boolean update(T obj, String tableName, String idColumnName, boolean needParent) throws IllegalArgumentException, IllegalAccessException {
		Class<?> clazz = obj.getClass();
		// 生成update脚本
		StringBuilder updateSQL = buildUpdateSql(clazz, tableName, idColumnName);
		//
		Map<String, Object> paramMap = new HashMap<String, Object>();
		invokeClazz (clazz, obj, paramMap, null);
		if(needParent){
			// 加载父类属性
			invokeClazz(clazz.getSuperclass(), obj, paramMap, null);
		}
		
		int rowNum = jdbcTemplate.update(updateSQL.toString(), paramMap);
		return rowNum > 0;
	}
	

	/**
	 * 
	 * @param obj
	 * @param needParent
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> Map<String, Object> invokeValue2Map(T obj,boolean needParent,String[] excludingNameArray) throws IllegalArgumentException, IllegalAccessException{
		Class<?> clazz = obj.getClass();
		Map<String, Object> paramMap = new HashMap<String, Object>();
		// 加载本类属性
		invokeClazz (clazz, obj, paramMap, excludingNameArray);
		if(needParent){
			// 加载父类属性
			invokeClazz(clazz.getSuperclass(), obj, paramMap, excludingNameArray);
		}
		return paramMap;
	}
	
	/**
	 * 批量新增操作
	 * 
	 * @param list<T>
	 * @param tableName
	 * @param excludingNameArray
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> void batchInsert(List<T> list, Class<T> clazz, String tableName, String[] excludingNameArray, boolean needParent) throws IllegalArgumentException, IllegalAccessException{
		// 
		List<Map<String, Object>> lm = new ArrayList<Map<String, Object>>();
		for(T t : list){
			
			Map<String, Object> paramMap = invokeValue2Map(t, needParent, excludingNameArray);
			lm.add(paramMap);
		}
		// 通过class映射成insert脚本
		StringBuilder sql = buildInsertSql(clazz, tableName, excludingNameArray);
		jdbcTemplate.batchUpdate(sql.toString(), getListObjectData(lm));
	}
	
	/**
	 * 批量新增操作
	 * 
	 * @param list<T>
	 * @param tableName
	 * @param excludingNameArray
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	protected <T> void batchInsert(List<T> list, Class<T> clazz, String tableName, String excludingName, boolean needParent) throws IllegalArgumentException, IllegalAccessException{
		if (StringUtils.isNotBlank(excludingName)) {
			batchInsert(list, clazz, tableName, new String[]{excludingName}, needParent);
		}
	}
	
	/**
	 * 
	 * @param list
	 * @return
	 */
	public static Map<String, Object>[] getArrayData(List<Map<String, Object>> list) {
		Map<String, Object>[] maps = new HashMap[list.size()];
		Iterator<Map<String, Object>> iterator = list.iterator();
		int i = 0;
		while (iterator.hasNext()) {
			Map<String, Object> map = (Map<String, Object>) iterator.next();
			maps[i++] = map;
		}
		return maps;
	}
	
	
	/**
	 * 
	 * @param list
	 * @return
	 */
	public static List<Object[]> getListObjectData(List<Map<String, Object>> list) {
		List<Object[]> backList = new ArrayList<Object[]>();
		Iterator<Map<String, Object>> iterator = list.iterator();
		int i = 0;
		while (iterator.hasNext()) {
			Map<String, Object> map = (Map<String, Object>) iterator.next();
			
			List<Object> temp = new ArrayList<Object>();
			
			for (Map.Entry<String, Object> entry : map.entrySet()) {
				temp.add(entry.getValue());
			}
			
			backList.add(temp.toArray());

		}
		return backList;
	}
	
	
	/**
	 * get page list
	 * @param pageNum start from 1
	 * @param itemCount
	 * @param sql
	 * @param sqlParams
	 * @param orderField
	 * @param orderType
	 * @param rowMapper
	 * @return
	 */
	@Override
	public PageModel getPageList(Integer pageNum, Integer itemCount, String sql, Object[] sqlParams, String orderField,String orderType, RowMapper rowMapper){
		
		if (pageNum == null || pageNum < 1) {
			pageNum = 1;
		}
		if (itemCount == null || itemCount <= 0) {
			itemCount = 10;
		}
		
		//get the total count sql
		String getTotalSql = "SELECT COUNT(0) FROM (" + sql + ") V";
		
		//get the records sql
		if(StringUtils.isNotEmpty(orderField)){
			sql += " ORDER BY " + orderField + " " + orderType;
		}
		
		String getResultSql = buildPageSql(Integer.toString(pageNum), Integer.toString(itemCount), sql);
		
		int totalCount = jdbcTemplate.queryForInt(getTotalSql, sqlParams);
		
		List resultList = null;
		if(rowMapper == null){
			resultList =  jdbcTemplate.queryForList(getResultSql, sqlParams);
		}else{
			resultList =  jdbcTemplate.query(getResultSql, sqlParams, rowMapper);
		}
		
		PageModel pageModel = new PageModel(pageNum, itemCount, totalCount, resultList);
		
        return pageModel;
	
	}
}
